package com.platform.boot.web.config.shiro;

import java.text.ParseException;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.platform.common.cache.redis.util.RedisUtil;
import com.platform.common.utils.DateHelper;
import com.platform.common.utils.ThreadManager;
import com.platform.system.api.entity.sys.User;
import com.platform.system.api.service.sys.UserService;

/**
 * 登陆次数限制
 */
public class CredentialsMatcher extends SimpleCredentialsMatcher {
	private static final Logger logger = Logger.getLogger(CredentialsMatcher.class);
    public static final String DEFAULT_RETRYLIMIT_CACHE_KEY_PREFIX = "shiro:cache:retrylimit:";
    private String retrylimit = DEFAULT_RETRYLIMIT_CACHE_KEY_PREFIX;
    public static final String DEFAULT_TIME_CACHE_KEY_PREFIX = "shiro:cache:time:";
    private String time = DEFAULT_TIME_CACHE_KEY_PREFIX;
	@Autowired
	UserService userService;
	@Autowired
	private RedisUtil redisUtil;
	
    private String getRedisRetrylimitKey(String username) {
        return this.retrylimit + username;
    }
    
    private String getRedisTimeKey(String username) {
        return this.time + username;
    }
    
	@Override
	public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
		UsernamePasswordToken utoken = (UsernamePasswordToken) token;
		// 获得用户输入的密码:(可以采用加盐(salt)的方式去检验)
//		String inPassword = new String(utoken.getPassword());
		// 获得数据库中的密码
//		String dbPassword = (String) info.getCredentials();
		// 进行密码的比对
//		return this.equals(ShiroMD5Util.encrypt(inPassword), dbPassword);
		
		 //获取用户名
        String username = (String)utoken.getPrincipal();
		String password = new String((char[]) token.getCredentials());
        User user = getUser(username);
        Subject currentUser = SecurityUtils.getSubject();
        Session session = currentUser.getSession();
        if(!(user!=null&&(user.getLock()).equals("2")))////1 没有锁定 2 已锁定
        	throw new LockedAccountException("帐号已锁定");
        //获取用户登录次数
        int i=redisUtil.get(getRedisRetrylimitKey(username))!=null? (int) redisUtil.get(getRedisRetrylimitKey(username)):0;
        AtomicInteger retryCount = new AtomicInteger(i);
        if (retryCount.incrementAndGet() > 5) {
            //如果用户登陆失败次数大于5次 抛出锁定用户异常  并修改数据库字段(根据用户名 锁定用户)
			try {
				lockAccount(username,"1");// 1 没有锁定 2 已锁定
			} catch (ParseException e) {
				e.printStackTrace();
			}
            logger.info("锁定用户" + username);
            //抛出用户锁定异常
            throw new LockedAccountException();
        }
        //判断用户账号和密码是否正确
        boolean matches = super.doCredentialsMatch(token, info);
        if (matches) {
        	try {
				lockAccount(username,"2");
			} catch (ParseException e) {
				e.printStackTrace();
			}
        }else{
        	//如果用户没有登陆过,登陆次数加1 并放入缓存
        	redisUtil.set(getRedisRetrylimitKey(username), retryCount.getAndIncrement());
        }
        return matches;
	}
	
    /**
     * 根据用户名 解锁/锁定用户
     * @param username
     * @return
     * @throws ParseException 
     */
     private void lockAccount(String username,String isLock) throws ParseException{
		User user = getUser(username);
        if (user != null){
        	User u  = new User();
        	u.setId(user.getId());
            u.setLock(isLock);
            u.setUpdateTime(new Date());
            userService.updateById(u);
            if("2".equals(isLock))//正确,从缓存中将用户登录计数 清除
            	redisUtil.remove(getRedisRetrylimitKey(username));
            else{
            	//还原状态
            	ThreadManager.execute(new Runnable() {
					@Override
					public void run() {
						User u  = new User();
			        	u.setId(user.getId());
			            u.setLock("2");
			            u.setUpdateTime(new Date());
			            userService.updateById(u);
					}
				},DateHelper.parseDate(DateHelper.getOldOrNewTime(15)));
            }
        }
    }
    
    
   private User getUser(String username){
    	Wrapper<User> wrapper =new EntityWrapper<>();
		wrapper.where("email = {0}", username);
		return userService.selectOne(wrapper);
    }
}
